<?php

namespace app\controller\pos;

use app\model\cms\Document;
use app\event\im\Robot as RobotEvent;
use app\model\im\ChatContent;
use app\event\store\User as UserEvent;
use app\model\ucenter\User as UserModel;
use app\model\im\Robot as RobotModel;
use app\event\app\Device as DeviceEvent;
use app\model\im\Chat as ChatModel;
use think\Exception;
use think\facade\Cache;
use think\facade\Log;
use think\worker\Server as ServerWorker;
use app\event\im\Chat as ChatEvent;
use Workerman\Connection\TcpConnection;
use Workerman\Lib\Timer;

class Server extends ServerWorker
{
    // 心跳时间
    protected int $hear_beat_time = 30;
    protected $port = 2345;
    protected $host = '0.0.0.0';
    protected $protocol = 'websocket';

//		protected $context  = [
//			'ssl' => [
//				// 请使用绝对路径
//				'local_cert'        => '/www/server/panel/vhost/ssl/kf.motonoaji.com/fullchain.pem', // 也可以是crt文件
//				'local_pk'          => '/www/server/panel/vhost/ssl/kf.motonoaji.com/privkey.pem',
//				'verify_peer'       => false,
//				'allow_self_signed' => false, //如果是自签名证书需要开启此选项
//			]
//		];
//
//		protected $port = 30031;
//		protected $option   = [
//			'transport'=>'ssl'
//		];


    protected function init()
    {

    }

    /**
     * 每个进程启动
     * @param $worker
     */
    public function onWorkerStart($worker)
    {
        // 1秒一次轮询
        Timer::add(1, function () use ($worker) {
            foreach ($worker->connections as $connection) {
                // 心跳控制，确认没有断线
                $this->_onlineCheck($connection);
                // 轮询任务处理
                if (isset($connection->uid) && Cache::has('worker_jobs_' . $connection->uid)) {
                    $this->_WorkerJobs($connection);
                }
            }
        });
        // 10秒一次轮询
        Timer::add(10, function () use ($worker) {
//            $event = new ChatEvent();

        });
        // 60秒一次轮询
        Timer::add(5, function () use ($worker) {
//            ChatEvent::timeoutTips();
            // ChatEvent::timeoutClose();
        });
    }


    /**
     * 设备连接成功
     * @param TcpConnection $connection
     * @return mixed
     */
    public function onConnect(TcpConnection $connection)
    {
        $connection->connection_time = time();
        return $this->_send('Connection Success!', $connection, 'init');
    }

    /**
     * 收到消息
     * @param TcpConnection $connection
     * @param string $parameter
     * @return mixed
     */
    public function onMessage(TcpConnection $connection, string $parameter)
    {
        $trace = [];
        try {
            $connection->last_message_time = time();
            if (!self::dataCheck($parameter)) {
                throw new Exception('data error');
            }
            $param = json_decode($parameter, true);
            $type = $param['type'];
            $this->_log($parameter, $type);
            $token = $param['token'];
            $data = $param['data'];
            // 发送消息
            if ('bind_id' == $type) {
                // 查询用户
//                $user = UserModel::where('id', $data['data']['id'])->find();
                    $result = ['message' => '绑定成功', 'code' => 0];
            }elseif ('ping' == $type) {
                // 缓存在线
                isset($connection->uid) && ChatEvent::setOnline($connection->uid);
                // 输出获取到的参数
//                echo "连接时URL参数: " . $query . "\n";
                $ip = $connection->getLocalAddress();
                $result = 'success' . $ip;
            } else {
                $result = '未知消息类型';
                $type = 'error';
            }
        } catch (\Exception $e) {
            $result = 'file:' . $e->getFile() . ',message:' . $e->getMessage() . ',line:' . $e->getMessage();
            $type = 'exception';
        }
        if (!(is_string($result) && 'no' == $result)) {
            $this->_send($result, $connection, $type, $trace);
        }
    }


    protected function _log($data, $data_type = 'message', $type = 'info')
    {
        $data_type != 'ping' && (Log::channel('socket')->record($data, $type) || dump($type . $data));
    }

    /**
     *  缓存发送AI消息
     * @param $data
     * int $delay
     */
    protected function _addDelayMessageJob($data, $connection, $delay = 5): void
    {
        if ($delay > 0) {
            $connection->delay['ai_single_chat'][] = ['data' => $data, 'time' => time() + $delay, 'type' => 'ai_single_chat'];
            $this->_send(['ai' => 1], $connection, 'entering');
        } else {
            $this->_sendChatAiMessage($data, $connection);
        }
    }

    /**
     *  发送AI消息
     * @param $data
     * int $delay
     */
    protected function _sendChatAiMessage($data, $connection)
    {
        $event = new ChatEvent();
        // 发送消息
        $re = $event->aiSend($data);
        if (0 == $re['code']) {
            if (!empty($data['bind_robot_id'])) {
                $connection->robot_id = $data['bind_robot_id'];
            } else {
                $connection->robot_id = null;
            }
            if ($connection->user['type'] == 99) {
                // AI客服发送消息
                $connection->last_service_time = time();
                $connection->wait = 'customer';
            }
            $this->_send($re['data'], $connection, 'receive_single_chat');
        } else {
            echo $re['message'] . "\r\n";
        }
    }


    /**
     *  设置机器人接待
     * int $delay
     */
    protected function _initRobot($connection): void
    {
        $connection->delay['ai_auto_message'] = RobotModel::where('type', 'in', '3,4,5')
            ->where('store_app_id', $connection->user['store_app_id'])
            ->where('status', 1)->select();
    }


    /**
     *  发送上线提醒
     * @param array $data
     * @param TcpConnection $fromConnection
     */
    protected function _sendLoginMessage(array $data, $fromConnection)
    {
        if (isset($data['to_user_id'])) {
            foreach ($this->worker->connections as $index => $connection) {
                if (isset($connection->uid) && $connection->uid == $data['to_user_id']) {
                    $this->_send(['user_id' => $fromConnection->uid, 'chat_id' => $data['to_chat_id']], $connection, 'user_login_success');
                }
            }
        }
    }


    public function onWorkerReload($worker)
    {
        // 重启服务
    }


    protected function _onlineCheck($connection)
    {
        //有可能该connection还没收到过消息，则last_message_time设置为当前时间
        if (!isset($connection->last_message_time)) {
            $connection->last_message_time = time();
        }
        $range = time() - $connection->last_message_time;
        // 上次通讯时间间隔大于心跳间隔，则认为客户端已经下线，关闭连接
        if ($range > (1 + $this->hear_beat_time)) {
//            if (isset($connection->uid)) {
//                ChatEvent::removeOnline($connection->uid);
//            }
            $connection->close();
            echo 'close ' . $connection->id . "\r\n";
        }
    }


    protected function _WorkerJobs($connection)
    {
        $jobs = Cache::pull('worker_jobs_' . $connection->uid);
        foreach ($jobs as $job) {
            if (isset($job['type'])) {
                switch ($job['type']) {
//                    case 'timeout_close':
//                    case 'timeout_tips':
//                        // 超时消息，关闭对话
//                        $chat = ChatModel::with('to_user')->where('id', $job['chat_id'])->find();
//                        if ($chat) {
//                            $this->_send(['chat' => $chat], $connection, $job['type']);
//                        }
//                        break;
                    case 'customer_access':
                        $chats = ChatModel::with('to_user')->where('id', 'in', $job['chat_id'])->find();
                        if ($chats) {
                            $this->_send(['chats' => $chats], $connection, 'new_access_lists');
                        }
                        break;
                    case 'single_chat':
                        $content = ChatContent::with('user')->where('id', $job['content_id'])->find();
                        if ($content) {
                            $this->_send($content, $connection, 'receive_single_chat');
                        }
                        break;
                    case 'switch_service':
                        $content = ChatContent::with('user')->where('id', $job['content_id'])->find();
                        if ($content) {
                            $this->_send($content, $connection, 'switch_service');
                        }
                        break;
                    case 'system_tips':
                        echo 'system_tips';
                        $this->_send(['content' => $job['content']], $connection, 'system_tips');
                        break;
                    case 'invite_comment':
                        $this->_send(['content' => $job], $connection, 'invite_comment');
                        break;
                    default:
                        dump($job);

                }

            } else {
                //无
            }
        }
    }

    /**
     * 断开设备
     * @param TcpConnection $connection
     */
    public function onClose($connection)
    {
        if (isset($connection->uid)) {
            echo 'close ' . $connection->uid . "\r\n";
            $user = UserModel::where('id', $connection->uid)->find();
            if ($user && $user['type'] == 99) {
                // 是否有客服对话
                $chat = ChatModel::where('to_user_id', $connection->uid)->where('status', 1)->find();
                if ($chat) {
                    foreach ($this->worker->connections as $index => $kf) {
                        if (isset($kf->uid) && $kf->uid == $chat['user_id']) {
                            $this->_send(['user_id' => $chat['to_user_id'], 'chat_id' => $chat['id']], $kf, 'user_logout_success');
                        }
                    }

                }
            }
            ChatEvent::removeOnline($connection->uid);
        }
//        $connection->close();

    }

    public function onError($connection, $code, $msg)
    {
        Log::channel('socket')->error('msg ' . $msg . ' error ' . $code);
    }

    /**
     * 发送指定格式数据
     * @param               $data
     * @param TcpConnection $connection
     * @param               $type
     * @param               $trace
     * @return mixed
     * @author 微尘 <yicmf@qq.com>
     */
    private function _send($data, $connection, $type, $trace = [])
    {
        $_data = [
            'cid' => $connection->id,
            'type' => $type,
            'data' => $data,
            'trace' => $trace
        ];
        //  $type = $request->get('type');
        //        $token = $request->get('token');
        //        $app = $request->get('app', 0);
        $this->_log('send ' . json_encode($_data), $type);
        return $connection->send(json_encode($_data));
    }

    private function dataCheck($data): bool
    {
        json_decode($data);
        return (json_last_error() == JSON_ERROR_NONE) && !is_numeric($data);
    }
}